package com.sc.demo2.common.config;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.sc.common.BaseWebInitializer;
import com.sc.common.annotations.EnableDistributedCaching;
import com.sc.common.annotations.EnableLocalCaching;
import com.sc.common.context.DefaultBusinessContext;
import com.sc.common.enums.ApplicationEnum;
import com.sc.common.enums.EhcacheKeyEnum;
import com.sc.common.enums.RedisKeyEnum;
import com.sc.common.util.SpringContextHolder;
import com.sc.common.util.cache.EhcacheTools;
import com.sc.common.util.cache.SpringRedisTools;
import net.sf.ehcache.CacheManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 系统启动，初始化相关数据的入口
 * Created by wust on 2019/6/12.
 */
@Component
public class WebInitializer extends BaseWebInitializer implements ApplicationListener<ApplicationReadyEvent> {

    static Logger logger = LogManager.getLogger(WebInitializer.class);


    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        DefaultBusinessContext.getContext().setDataSourceId(ApplicationEnum.DEFAULT.name());

        ConfigurableApplicationContext configurableApplicationContext = applicationReadyEvent.getApplicationContext();

        resetCached(configurableApplicationContext);

        intHandle(configurableApplicationContext);

        localCachingHandle(configurableApplicationContext);

        distributedCachingHandle(configurableApplicationContext);

        complexCachingHandle(configurableApplicationContext);
    }


    @Override
    protected String getEntityPath() {
        return "com/sc/common/entity/demo2/**/*.class";
    }


    @Override
    protected String[] getCacheNames() {
        ClassPathResource classPathResource = new ClassPathResource("ehcache.xml");
        try {
            CacheManager manager = new CacheManager(classPathResource.getInputStream());
            if(manager != null){
                String[] cacheNames = manager.getCacheNames();
                return cacheNames;
            }
        } catch (IOException e) {
        }
        return null;
    }

    @Override
    public void localCachingHandle(ConfigurableApplicationContext configurableApplicationContext) {
        EhcacheTools ehcacheTools = SpringContextHolder.getBean("ehcacheTools");

        List<Class> classList = new ArrayList<>(20);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
        Resource[] resources = null;
        try {
            resources = resolver.getResources("classpath*:com/sc/common/entity/demo2/**/*.class");
            for (Resource resource : resources) {
                MetadataReader reader = metaReader.getMetadataReader(resource);
                String className = reader.getClassMetadata().getClassName();
                Class<?> clazz = ClassLoaderUtil.loadClass(className.replace("BOOT-INF.classes",""));
                EnableLocalCaching enableLocalCaching = clazz.getAnnotation(EnableLocalCaching.class);
                if(enableLocalCaching != null){
                    classList.add(clazz);
                }
            }
        } catch (IOException e) {
            logger.error("系统启动时处理本地缓存失败：",e);
        }


        if(CollectionUtil.isNotEmpty(classList)){
            for (Class aClass : classList) {
                String source = aClass.getSimpleName();
                String beanName = source.substring(0, 1).toLowerCase() + source.substring(1) + "Mapper";
                Object bean = SpringContextHolder.getBean(beanName);
                try {
                    Method mtd = bean.getClass().getMethod("selectAll");
                    Object listObj = mtd.invoke(bean);
                    if (listObj != null) {
                        List list = (List) listObj;
                        for (Object o : list) {
                            if (o == null) {
                                continue;
                            }

                            Object primaryKey = ReflectUtil.getFieldValue(o, "id");
                            if (primaryKey == null) {
                                continue;
                            }

                            String localCachedKey = String.format(EhcacheKeyEnum.EHCACHE_KEY_SELECT_BY_PRIMARY_KEY.getStringValue(), source, Convert.toStr(primaryKey));
                            ehcacheTools.put("poCache",localCachedKey,o);
                        }
                    }
                } catch (NoSuchMethodException e) {
                    logger.error("系统启动时处理本地缓存失败，找不到方法异常：",e);
                } catch (IllegalAccessException e) {
                    logger.error("系统启动时处理本地缓存失败",e);
                } catch (InvocationTargetException e) {
                    logger.error("系统启动时处理本地缓存失败",e);
                }
            }
        }
    }

    @Override
    public void distributedCachingHandle(ConfigurableApplicationContext configurableApplicationContext) {
        SpringRedisTools springRedisTools = configurableApplicationContext.getBean(SpringRedisTools.class);

        List<Class> classList = new ArrayList<>(20);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
        Resource[] resources = null;
        try {
            resources = resolver.getResources("classpath*:com/sc/common/entity/demo2/**/*.class");
            for (Resource resource : resources) {
                MetadataReader reader = metaReader.getMetadataReader(resource);
                String className = reader.getClassMetadata().getClassName();
                Class<?> clazz = ClassLoaderUtil.loadClass(className.replace("BOOT-INF.classes",""));
                EnableDistributedCaching enableDistributedCaching = clazz.getAnnotation(EnableDistributedCaching.class);
                if(enableDistributedCaching != null){
                    classList.add(clazz);
                }
            }
        } catch (IOException e) {
            logger.error("系统启动时处理分布式缓存失败：",e);
        }


        if(CollectionUtil.isNotEmpty(classList)){
            for (Class aClass : classList) {
                String source = aClass.getSimpleName();
                String beanName = source.substring(0, 1).toLowerCase() + source.substring(1) + "Mapper";
                Object bean = SpringContextHolder.getBean(beanName);
                try {
                    Method mtd = bean.getClass().getMethod("selectAll");
                    Object listObj = mtd.invoke(bean);
                    if (listObj != null) {
                        List list = (List) listObj;
                        for (Object o : list) {
                            if (o == null) {
                                continue;
                            }

                            Object primaryKey = ReflectUtil.getFieldValue(o, "id");
                            if (primaryKey == null) {
                                continue;
                            }

                            String redisKey = String.format(RedisKeyEnum.REDIS_KEY_PRIMARY_SELECT_BY_PRIMARY_KEY.getStringValue(), source, Convert.toStr(primaryKey));
                            if (springRedisTools.hasKey(redisKey)) {
                                springRedisTools.deleteByKey(redisKey);
                            }
                            springRedisTools.addData(redisKey, JSONObject.toJSONString(o));
                        }
                    }
                } catch (NoSuchMethodException e) {
                    logger.error("系统启动时处理分布式缓存失败，找不到方法异常：",e);
                } catch (IllegalAccessException e) {
                    logger.error("系统启动时处理分布式缓存失败：",e);
                } catch (InvocationTargetException e) {
                    logger.error("系统启动时处理分布式缓存失败：",e);
                }
            }
        }
    }
}
